{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install -q xgboost==0.90\n",
    "!pip install -q stepfunctions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Setup\n",
    "\n",
    "### Add a policy to your SageMaker role in IAM\n",
    "\n",
    "**If you are running this notebook on an Amazon SageMaker notebook instance**, the IAM role assumed by your notebook instance needs permission to create and run workflows in AWS Step Functions. To provide this permission to the role, do the following.\n",
    "\n",
    "1. Open the Amazon [SageMaker console](https://console.aws.amazon.com/sagemaker/). \n",
    "2. Select **Notebook instances** and choose the name of your notebook instance\n",
    "3. Under **Permissions and encryption** select the role ARN to view the role on the IAM console\n",
    "4. Choose **Attach policies** and search for `AWSStepFunctionsFullAccess`.\n",
    "5. Select the check box next to `AWSStepFunctionsFullAccess` and choose **Attach policy**\n",
    "\n",
    "If you are running this notebook in a local environment, the SDK will use your configured AWS CLI configuration. For more information, see [Configuring the AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html).\n",
    "\n",
    "Next, create an execution role in IAM for Step Functions. \n",
    "\n",
    "### Create an execution role for Step Functions\n",
    "\n",
    "You need an execution role so that you can create and execute workflows in Step Functions.\n",
    "\n",
    "1. Go to the [IAM console](https://console.aws.amazon.com/iam/)\n",
    "2. Select **Roles** and then **Create role**.\n",
    "3. Under **Choose the service that will use this role** select **Step Functions**\n",
    "4. Choose **Next** until you can enter a **Role name**\n",
    "5. Enter a name such as `StepFunctionsWorkflowExecutionRole` and then select **Create role**\n",
    "\n",
    "\n",
    "Attach a policy to the role you created. The following steps attach a policy that provides full access to Step Functions, however as a good practice you should only provide access to the resources you need.  \n",
    "\n",
    "1. Under the **Permissions** tab, click **Add inline policy**\n",
    "2. Enter the following in the **JSON** tab\n",
    "\n",
    "```json\n",
    "{\n",
    "    \"Version\": \"2012-10-17\",\n",
    "    \"Statement\": [\n",
    "        {\n",
    "            \"Effect\": \"Allow\",\n",
    "            \"Action\": [\n",
    "                \"sagemaker:CreateTransformJob\",\n",
    "                \"sagemaker:DescribeTransformJob\",\n",
    "                \"sagemaker:StopTransformJob\",\n",
    "                \"sagemaker:CreateTrainingJob\",\n",
    "                \"sagemaker:DescribeTrainingJob\",\n",
    "                \"sagemaker:StopTrainingJob\",\n",
    "                \"sagemaker:CreateHyperParameterTuningJob\",\n",
    "                \"sagemaker:DescribeHyperParameterTuningJob\",\n",
    "                \"sagemaker:StopHyperParameterTuningJob\",\n",
    "                \"sagemaker:CreateModel\",\n",
    "                \"sagemaker:CreateEndpointConfig\",\n",
    "                \"sagemaker:CreateEndpoint\",\n",
    "                \"sagemaker:DeleteEndpointConfig\",\n",
    "                \"sagemaker:DeleteEndpoint\",\n",
    "                \"sagemaker:UpdateEndpoint\",\n",
    "                \"sagemaker:ListTags\",\n",
    "                \"lambda:InvokeFunction\",\n",
    "                \"sqs:SendMessage\",\n",
    "                \"sns:Publish\",\n",
    "                \"ecs:RunTask\",\n",
    "                \"ecs:StopTask\",\n",
    "                \"ecs:DescribeTasks\",\n",
    "                \"dynamodb:GetItem\",\n",
    "                \"dynamodb:PutItem\",\n",
    "                \"dynamodb:UpdateItem\",\n",
    "                \"dynamodb:DeleteItem\",\n",
    "                \"batch:SubmitJob\",\n",
    "                \"batch:DescribeJobs\",\n",
    "                \"batch:TerminateJob\",\n",
    "                \"glue:StartJobRun\",\n",
    "                \"glue:GetJobRun\",\n",
    "                \"glue:GetJobRuns\",\n",
    "                \"glue:BatchStopJobRun\"\n",
    "            ],\n",
    "            \"Resource\": \"*\"\n",
    "        },\n",
    "        {\n",
    "            \"Effect\": \"Allow\",\n",
    "            \"Action\": [\n",
    "                \"iam:PassRole\"\n",
    "            ],\n",
    "            \"Resource\": \"*\",\n",
    "            \"Condition\": {\n",
    "                \"StringEquals\": {\n",
    "                    \"iam:PassedToService\": \"sagemaker.amazonaws.com\"\n",
    "                }\n",
    "            }\n",
    "        },\n",
    "        {\n",
    "            \"Effect\": \"Allow\",\n",
    "            \"Action\": [\n",
    "                \"events:PutTargets\",\n",
    "                \"events:PutRule\",\n",
    "                \"events:DescribeRule\"\n",
    "            ],\n",
    "            \"Resource\": [\n",
    "                \"arn:aws:events:*:*:rule/StepFunctionsGetEventsForSageMakerTrainingJobsRule\",\n",
    "                \"arn:aws:events:*:*:rule/StepFunctionsGetEventsForSageMakerTransformJobsRule\",\n",
    "                \"arn:aws:events:*:*:rule/StepFunctionsGetEventsForSageMakerTuningJobsRule\",\n",
    "                \"arn:aws:events:*:*:rule/StepFunctionsGetEventsForECSTaskRule\",\n",
    "                \"arn:aws:events:*:*:rule/StepFunctionsGetEventsForBatchJobsRule\"\n",
    "            ]\n",
    "        }\n",
    "    ]\n",
    "}\n",
    "```\n",
    "\n",
    "3. Choose **Review policy** and give the policy a name such as `StepFunctionsWorkflowExecutionPolicy`\n",
    "4. Choose **Create policy**. You will be redirected to the details page for the role.\n",
    "5. Copy the **Role ARN** at the top of the **Summary**\n",
    "\n",
    "### Import the required modules from the SDK"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import boto3\n",
    "import sagemaker\n",
    "import pandas as pd\n",
    "\n",
    "sess   = sagemaker.Session()\n",
    "bucket = sess.default_bucket()\n",
    "role = sagemaker.get_execution_role()\n",
    "region = boto3.Session().region_name\n",
    "\n",
    "sm = boto3.Session().client(service_name='sagemaker', region_name=region)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%store -r spark_processing_job_s3_output_prefix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print('Previous Spark Processing Job Name: {}'.format(spark_processing_job_s3_output_prefix))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "prefix_train = '{}/output/tfidf-train'.format(spark_processing_job_s3_output_prefix)\n",
    "prefix_validation = '{}/output/tfidf-validation'.format(spark_processing_job_s3_output_prefix)\n",
    "prefix_test = '{}/output/tfidf-test'.format(spark_processing_job_s3_output_prefix)\n",
    "\n",
    "tfidf_train_path = './{}'.format(prefix_train)\n",
    "tfidf_validation_path = './{}'.format(prefix_validation)\n",
    "tfidf_test_path = './{}'.format(prefix_test)\n",
    "\n",
    "tfidf_train_s3_uri = 's3://{}/{}'.format(bucket, prefix_train)\n",
    "tfidf_validation_s3_uri = 's3://{}/{}'.format(bucket, prefix_validation)\n",
    "tfidf_test_s3_uri = 's3://{}/{}'.format(bucket, prefix_test)\n",
    "\n",
    "print(tfidf_train_s3_uri)\n",
    "print(tfidf_validation_s3_uri)\n",
    "print(tfidf_test_s3_uri)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "s3_input_train_data = sagemaker.s3_input(s3_data=tfidf_train_s3_uri, content_type='text/csv')\n",
    "s3_input_validation_data = sagemaker.s3_input(s3_data=tfidf_validation_s3_uri, content_type='text/csv')\n",
    "s3_input_test_data = sagemaker.s3_input(s3_data=tfidf_test_s3_uri, content_type='text/csv')\n",
    "\n",
    "print(s3_input_train_data.config)\n",
    "print(s3_input_validation_data.config)\n",
    "print(s3_input_test_data.config)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!aws s3 ls $tfidf_train_s3_uri/ "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!aws s3 ls $tfidf_validation_s3_uri/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!aws s3 ls $tfidf_test_s3_uri/"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!aws s3 cp --recursive $tfidf_train_s3_uri $tfidf_train_path\n",
    "!aws s3 cp --recursive $tfidf_validation_s3_uri $tfidf_validation_path\n",
    "!aws s3 cp --recursive $tfidf_test_s3_uri $tfidf_test_path"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!cat src/xgboost_reviews.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from sagemaker.xgboost import XGBoost\n",
    "\n",
    "# TODO:  Bug re: s3://s3://?  in just pipelines?  doesn't seem to be in ScriptMode\n",
    "#        See here for more info:  https://github.com/aws/aws-step-functions-data-science-sdk-python/issues/32\n",
    "model_output_path = 's3://{}/models/amazon-reviews/script-mode/training-runs'.format(bucket)\n",
    "\n",
    "xgb_estimator = XGBoost(entry_point='xgboost_reviews.py', \n",
    "                        source_dir='src/',\n",
    "                        role=role,\n",
    "                        train_instance_count=1, \n",
    "#                        train_instance_type='local',\n",
    "                        train_instance_type='ml.c5.4xlarge',\n",
    "                        framework_version='0.90-2',\n",
    "                        py_version='py3',\n",
    "                        output_path=model_output_path,\n",
    "                        hyperparameters={'objective':'binary:logistic',\n",
    "                                         'num_round': 1,\n",
    "                                         'max_depth': 5},\n",
    "                        enable_cloudwatch_metrics=True,\n",
    "                       )"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Build a training pipeline with the Step Functions SDK\n",
    "\n",
    "A typical task for a data scientist is to train a model and deploy that model to an endpoint. Without the Step Functions SDK, this is a four step process on SageMaker that includes the following.\n",
    "\n",
    "1. Training the model\n",
    "2. Creating the model on SageMaker\n",
    "3. Creating an endpoint configuration\n",
    "4. Deploying the trained model to the configured endpoint\n",
    "\n",
    "The Step Functions SDK provides the [TrainingPipeline](https://aws-step-functions-data-science-sdk.readthedocs.io/en/latest/pipelines.html#stepfunctions.template.pipeline.train.TrainingPipeline) API to simplify this procedure. The following configures `pipeline` with the necessary parameters to define a training pipeline.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# paste the StepFunctionsWorkflowExecutionRole ARN from above\n",
    "workflow_execution_role = \"arn:aws:iam::835319576252:role/StepFunctionsWorkflowExecutionRole\"\n",
    "#workflow_execution_role = \"XXXX\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from stepfunctions.template.pipeline import TrainingPipeline\n",
    "\n",
    "pipeline = TrainingPipeline(\n",
    "    estimator=xgb_estimator,\n",
    "    role=workflow_execution_role,\n",
    "    inputs={'train': s3_input_train_data, \n",
    "            'validation': s3_input_validation_data},\n",
    "    s3_bucket=bucket)\n",
    "#    s3_bucket=model_output_path)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Visualize the pipeline\n",
    "You can now view the workflow definition, and also visualize it as a graph. This workflow and graph represent your training pipeline.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### View the pipeline definition"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "print(pipeline.workflow.definition.to_json(pretty=True))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Visualize the pipeline graph"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipeline.render_graph()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create and execute the pipeline on AWS Step Functions\n",
    "\n",
    "Create the pipeline in AWS Step Functions with [create](https://aws-step-functions-data-science-sdk.readthedocs.io/en/latest/workflow.html#stepfunctions.workflow.Workflow.create)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "pipeline.create()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Run the pipeline \n",
    "\n",
    "A link will be provided after the following cell is executed. Following this link, you can monitor your pipeline execution on Step Functions' console."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "execution = pipeline.execute()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Waiting for this:  https://github.com/aws/aws-step-functions-data-science-sdk-python/issues/32\n",
    "\n",
    "execution.render_progress()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## *** YOU MUST WAIT FOR THE ABOVE PIPELINE TO COMPLETE BEFORE CONTINUING! ***"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Review the execution events"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import json\n",
    "events = execution.list_events()\n",
    "\n",
    "event_output = json.loads(events[21]['stateExitedEventDetails']['output'])\n",
    "endpoint_arn = event_output['EndpointArn']\n",
    "\n",
    "endpoint_name = json.loads(events[18]['taskScheduledEventDetails']['parameters'])['EndpointName']\n",
    "endpoint_name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "predictor = sagemaker.xgboost.model.XGBoostPredictor(endpoint_name=endpoint_name)\n",
    "predictor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Deploy Endpoint\n",
    "\n",
    "From an external application, you can use the following code to make a prediction"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# TODO:  This is erroring out with `Please provide a model_fn implementation.`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import boto3\n",
    "import sagemaker\n",
    "import pandas as pd\n",
    "\n",
    "sess   = sagemaker.Session()\n",
    "bucket = sess.default_bucket()\n",
    "role = sagemaker.get_execution_role()\n",
    "region = boto3.Session().region_name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# import time\n",
    "\n",
    "# # https://towardsdatascience.com/xgboost-in-amazon-sagemaker-28e5e354dbcd\n",
    "# from sagemaker.predictor import csv_serializer\n",
    "\n",
    "# xgb_endpoint_name = 'xgboost-script-pipeline-{}'.format(time.strftime(\"%Y-%m-%d-%H-%M-%S\", time.gmtime()))\n",
    "# xgb_endpoint_name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# ## Deploy trained XGBoost model endpoint to perform predictions\n",
    "# xgb_predictor = xgb_estimator.deploy(initial_instance_count = 1, \n",
    "#                                      instance_type = 'ml.m4.xlarge',\n",
    "#                                      endpoint_name=xgb_endpoint_name)\n",
    "\n",
    "# xgb_predictor.content_type = 'text/csv'\n",
    "# xgb_predictor.serializer = csv_serializer\n",
    "# xgb_predictor.deserializer = None"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "from sklearn.metrics import accuracy_score, precision_score, classification_report, confusion_matrix\n",
    "\n",
    "sm_runtime = boto3.client('sagemaker-runtime')\n",
    "\n",
    "#payload_500_samples = X_test[:500].to_csv(index=False, header=False).rstrip()\n",
    "\n",
    "# response_500_samples = sm_runtime.invoke_endpoint(\n",
    "#     EndpointName=endpoint_name,\n",
    "#     Body=payload.encode('utf-8'),\n",
    "#     ContentType='text/csv')['Body'].read()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# predictions_500_samples = np.fromstring(response_500_samples, sep=',')\n",
    "# predictions_500_samples_0_or_1 = np.where(predictions_500_samples > 0.5, 1, 0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# print('Test Accuracy: ', accuracy_score(y_test[:500], predictions_500_samples_0_or_1))\n",
    "# print('Test Precision: ', precision_score(y_test[:500], predictions_500_samples_0_or_1, average=None))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# import seaborn as sn\n",
    "# import pandas as pd\n",
    "# import matplotlib.pyplot as plt\n",
    "\n",
    "# df_cm_test = confusion_matrix(y_test[:500], predictions_500_samples_0_or_1)\n",
    "# df_cm_test"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# import itertools\n",
    "\n",
    "# import matplotlib.pyplot as plt\n",
    "# %matplotlib inline\n",
    "# %config InlineBackend.figure_format='retina'\n",
    "\n",
    "# def plot_conf_mat(cm, classes, title, cmap = plt.cm.Greens):\n",
    "#     print(cm)\n",
    "#     plt.imshow(cm, interpolation='nearest', cmap=cmap)\n",
    "#     plt.title(title)\n",
    "#     plt.colorbar()\n",
    "#     tick_marks = np.arange(len(classes))\n",
    "#     plt.xticks(tick_marks, classes, rotation=45)\n",
    "#     plt.yticks(tick_marks, classes)\n",
    "\n",
    "#     fmt = 'd'\n",
    "#     thresh = cm.max() / 2.\n",
    "#     for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n",
    "#         plt.text(j, i, format(cm[i, j], fmt),\n",
    "#         horizontalalignment=\"center\",\n",
    "#         color=\"black\" if cm[i, j] > thresh else \"black\")\n",
    "\n",
    "#         plt.tight_layout()\n",
    "#         plt.ylabel('True label')\n",
    "#         plt.xlabel('Predicted label')\n",
    "\n",
    "# # Plot non-normalized confusion matrix\n",
    "# plt.figure()\n",
    "# fig, ax = plt.subplots(figsize=(6,4))\n",
    "# plot_conf_mat(df_cm_test, classes=['Not Positive Sentiment', 'Positive Sentiment'], \n",
    "#                           title='Confusion matrix')\n",
    "# plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# from sklearn import metrics\n",
    "\n",
    "# import matplotlib.pyplot as plt\n",
    "# %matplotlib inline\n",
    "# %config InlineBackend.figure_format='retina'\n",
    "\n",
    "# auc = round(metrics.roc_auc_score(y_test, preds_test), 4)\n",
    "# print('AUC is ' + repr(auc))\n",
    "\n",
    "# fpr, tpr, _ = metrics.roc_curve(y_test, preds_test)\n",
    "\n",
    "# plt.title('ROC Curve')\n",
    "# plt.plot(fpr, tpr, 'b',\n",
    "# label='AUC = %0.2f'% auc)\n",
    "# plt.legend(loc='lower right')\n",
    "# plt.plot([0,1],[0,1],'r--')\n",
    "# plt.xlim([-0.1,1.1])\n",
    "# plt.ylim([-0.1,1.1])\n",
    "# plt.ylabel('True Positive Rate')\n",
    "# plt.xlabel('False Positive Rate')\n",
    "# plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "payload = \"\"\"Very funny. A typical mid 50's comedy.\"\"\"\n",
    "sm_runtime.invoke_endpoint(\n",
    "     EndpointName=endpoint_name,\n",
    "     Body=payload.encode('utf-8'),\n",
    "     ContentType='text/csv')['Body'].read()\n",
    "    \n",
    "\n",
    "#predictions, raw_outputs = xgb_predictor.predict([\"\"\"Very funny. A typical mid 50's comedy.\"\"\"])\n",
    "print('Predictions: {}'.format(predictions))\n",
    "print('Raw outputs: {}'.format(raw_outputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "predictions, raw_outputs = xgb_predictor.predict([\"\"\"That movie was absolutely awful.\"\"\"])\n",
    "print('Predictions: {}'.format(predictions))\n",
    "print('Raw outputs: {}'.format(raw_outputs))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "conda_python3",
   "language": "python",
   "name": "conda_python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
